home *** CD-ROM | disk | FTP | other *** search
/ SuperHack / SuperHack CD.bin / CODING / CPP / METAKIT.ZIP / EXAMPLES / CATFISH / SCANDISK.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1996-12-09  |  10.5 KB  |  325 lines

  1. //  scandisk.cpp  -  recursive directory scanner sample code
  2. //
  3. //  This is a part of the MetaKit library.
  4. //  Copyright (c) 1996 Meta Four Software.
  5. //  All rights reserved.
  6. /////////////////////////////////////////////////////////////////////////////
  7. //
  8. //  The following two globally accesible routines are defined below:
  9. //                  
  10. //      c4_View fScanDirectories(const char* path_);
  11. //      CString fFullPath(c4_View& dirs_, int dirNum_);
  12. //      
  13. //  The fScanDirectories routine does all the work, and completely hides all
  14. //  Windows 16/32 specifics by returning a generalized catalog tree object.
  15. //  In contrast with normal C++ conventions, nearly all MetaKit objects are
  16. //  reference counted. This takes care of fully automatic cleanup after use.
  17. //  
  18. //  The structure of the object returned by fScanDirectories is as follows:
  19. //  
  20. //      Property        Type        Description
  21. //      ========        ====        ===========
  22. //          
  23. //      dirs            nested      Contains 1 record per directory
  24. //          parent      integer     Index of parent entry
  25. //          name        string      Name of this directory
  26. //          files       nested      Contains 1 record per file entry
  27. //              name    string      Name of this file
  28. //              size    string      File size
  29. //              date    integer     Modification date (DOS format 7+4+5 bits)
  30. //  
  31. //  The first directory entry (entry 0) is special: the parent is set to zero
  32. //  (points to itself), and the name is the base path name (see path_ arg).
  33. //              
  34. //  In C++ notation, a declaration for this object would be something like:
  35. //  
  36. //      struct
  37. //      {
  38. //          int parent;
  39. //          char* name;
  40. //  
  41. //          struct
  42. //          {
  43. //              char* name;
  44. //              long size;
  45. //              int date;
  46. //  
  47. //          } files [];                 // variable size, not allowed in C++
  48. //  
  49. //      } dirs [];
  50. //
  51. /////////////////////////////////////////////////////////////////////////////
  52.  
  53. #include "stdafx.h"
  54. #include "scandisk.h"
  55.  
  56. #ifndef _WIN32
  57.     #define LFNAMES // define this to support long filenames in a 16-bit app
  58. #endif
  59.  
  60. /////////////////////////////////////////////////////////////////////////////
  61. // Property definitions
  62.  
  63.     c4_ViewProp     pFiles ("files");
  64.     c4_IntProp      pParent ("parent"),
  65.                     pSize ("size"),
  66.                     pDate ("date");
  67.     c4_StringProp   pName ("name");
  68.  
  69. /////////////////////////////////////////////////////////////////////////////
  70.  
  71. bool cStatusHandler::UpdateStatus(const char* /* text */)
  72. {
  73.     MSG msg;
  74.     while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
  75.     {
  76.         if (msg.message == WM_QUIT)
  77.         {
  78.             ::PostQuitMessage(msg.wParam);
  79.             return FALSE;
  80.         }
  81.         if (!AfxGetApp()->PreTranslateMessage(&msg))
  82.         {
  83.             ::TranslateMessage(&msg);
  84.             ::DispatchMessage(&msg);
  85.         }
  86.     }
  87.     AfxGetApp()->OnIdle(0);   // updates user interface
  88.     AfxGetApp()->OnIdle(1);   // frees temporary objects
  89.     
  90.     return TRUE;
  91. }
  92.  
  93. /////////////////////////////////////////////////////////////////////////////
  94. // Reconstruct the full path name from a subdirectory index in the tree
  95.  
  96. CString fFullPath(c4_View& dirs_, int dirNum_)
  97. {
  98.         // Prefix all parent dir names until the root level is reached
  99.     CString path;
  100.     for (;;)
  101.     {
  102.         path = pName (dirs_[dirNum_]) + "\\" + path;
  103.  
  104.         if (dirNum_ == 0)
  105.             break;
  106.             
  107.         dirNum_ = (int) pParent (dirs_[dirNum_]);
  108.     }
  109.  
  110.     return path; // this result always has a trailing backslash
  111. }
  112.  
  113. /////////////////////////////////////////////////////////////////////////////
  114. // There are two versions of ScanSubDir, one for Win32 and one for Win16
  115.  
  116. #ifdef _WIN32
  117.  
  118.         // Convert a Win32 filedate back to the good old DOS format
  119.     static WORD DosDate(FILETIME& ft)
  120.     {
  121.         SYSTEMTIME st;
  122.         
  123.         if (!FileTimeToSystemTime(&ft, &st))
  124.             return 0;
  125.             
  126.         return (WORD) (((st.wYear-1980) << 9) | (st.wMonth << 5) | st.wDay);
  127.     }
  128.     
  129.         // Scan subdirectory and update the directory structure
  130.     static void ScanSubDir(c4_View& dirs_, int dirNum_, const CString& path_)
  131.     {
  132.         WIN32_FIND_DATA fd;
  133.         
  134.         HANDLE h = FindFirstFile(path_ + "*", &fd);
  135.         if (h)
  136.         {
  137.                 //  Some notes on efficiency:
  138.                 //
  139.                 //  1)  It is probably faster to fill a view with data, and
  140.                 //      then store it in a persistent field, than to modify
  141.                 //      persistent structures in place. In this example, this
  142.                 //      is needed anyway, since we also sort all filenames.
  143.                 //
  144.                 //  2)  If possible, avoid constructing rows inside a loop.
  145.                 //      For that reason, both 'prop [const]' and 'row + row'
  146.                 //      are relatively inefficient.
  147.  
  148.         //  c4_View files = pFiles (dirs_[dirNum_]);
  149.             c4_View files;
  150.             c4_Row dir, file;
  151.  
  152.             do
  153.             {
  154.                 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  155.                 {
  156.                     if (fd.cFileName[0] == '.')
  157.                         continue;
  158.  
  159.                 //  dirs_.Add(pParent [dirNum_] + pName [fd.cFileName]);
  160.                     pParent (dir) = dirNum_;
  161.                     pName (dir) = fd.cFileName;
  162.                     dirs_.Add(dir);
  163.                 }            
  164.                 else
  165.                 {
  166.                 //  files.Add(pName [fd.cFileName] + pSize [fd.nFileSizeLow]
  167.                 //              + pDate [DosDate(fd.ftLastWriteTime)]);
  168.                     pName (file) = fd.cFileName;
  169.                     pSize (file) = fd.nFileSizeLow;
  170.                     pDate (file) = DosDate(fd.ftLastWriteTime);
  171.                     files.Add(file);
  172.                 }
  173.                 
  174.             } while (FindNextFile(h, &fd));
  175.             
  176.             FindClose(h);
  177.             
  178.             pFiles (dirs_[dirNum_]) = files.SortOn(pName);
  179.         }
  180.     }
  181.  
  182. #else
  183.  
  184.     #include <dos.h>
  185.     
  186.         // Scan subdirectory and update the directory structure
  187.     static void ScanSubDir(c4_View& dirs_, int dirNum_, const CString& path_)
  188.     {
  189.         _find_t fd;
  190.         
  191.         const unsigned mask = _A_RDONLY | _A_HIDDEN | _A_SYSTEM | _A_SUBDIR;
  192.         if (_dos_findfirst(path_ + "*.*", mask, &fd) == 0)
  193.         {
  194.             c4_View files;
  195.             c4_Row dir, file;
  196.  
  197.             do
  198.             {
  199.                 if (fd.attrib & _A_SUBDIR)
  200.                 {
  201.                     if (fd.name[0] == '.')
  202.                         continue;
  203.  
  204.                     pParent (dir) = dirNum_;
  205.                     pName (dir) = fd.name;
  206.                     dirs_.Add(dir);
  207.                 }            
  208.                 else
  209.                 {
  210.                     pName (file) = fd.name;
  211.                     pSize (file) = fd.size;
  212.                     pDate (file) = fd.wr_date;
  213.                     files.Add(file);
  214.                 }
  215.                 
  216.             } while (_dos_findnext(&fd) == 0);
  217.             
  218.             pFiles (dirs_[dirNum_]) = files.SortOn(pName);
  219.         }
  220.     }
  221.  
  222. #endif
  223.     
  224. /////////////////////////////////////////////////////////////////////////////
  225. // The following variation supports long filenames, as used by Windows 95
  226.  
  227. #ifdef LFNAMES
  228.  
  229.     #include "lfnames.h"
  230.  
  231.         // Scan subdirectory and update the directory structure
  232.     static void ScanLongDir(c4_View& dirs_, int dirNum_, const CString& path_)
  233.     {
  234.         lfn_find_t fd;
  235.         
  236.         const int mask = _A_RDONLY | _A_HIDDEN | _A_SYSTEM | _A_SUBDIR;
  237.         if (lfn_findfirst(path_ + "*.*", mask, &fd) == 0)
  238.         {
  239.             c4_View files;
  240.             c4_Row dir, file;
  241.  
  242.             do
  243.             {
  244.                 CString s = fd.name;
  245.                 s.MakeUpper();
  246.  
  247.                     // if only upper case, then capitalize the first letter only
  248.                 if (s == fd.name && !s.IsEmpty())
  249.                 {
  250.                     char c = s[0];
  251.                     s.MakeLower();
  252.                     s.SetAt(0, c);
  253.                 }
  254.                 else
  255.                     s = fd.name;
  256.                     
  257.                 if (fd.attrib & _A_SUBDIR)
  258.                 {
  259.                     if (fd.name[0] == '.')
  260.                         continue;
  261.  
  262.                     pParent (dir) = dirNum_;
  263.                     pName (dir) = s;
  264.                     dirs_.Add(dir);
  265.                 }            
  266.                 else
  267.                 {
  268.                     pName (file) = s;
  269.                     pSize (file) = fd.size;
  270.                     pDate (file) = fd.wr_date;
  271.                     files.Add(file);
  272.                 }
  273.                 
  274.             } while (lfn_findnext(&fd) == 0);
  275.             
  276.             lfn_findclose(&fd);
  277.             
  278.             pFiles (dirs_[dirNum_]) = files.SortOn(pName);
  279.         }
  280.     }
  281.  
  282. #endif
  283.  
  284. /////////////////////////////////////////////////////////////////////////////
  285. // Scan a directory tree and return a corresponding structure for it
  286.  
  287. c4_View fScanDirectories(const char* path_, cStatusHandler* handler)
  288. {
  289.         // Start with a view containing the root directory entry
  290.     c4_View dirs;
  291.     dirs.Add(pName [path_]);
  292.     
  293. #ifdef LFNAMES
  294.         // don't call lfn_api on Win 3.1x, it will crash...
  295. //  BOOL useLfns = LOBYTE(LOWORD(GetVersion())) > 3 && lfn_api(path_) != 0;
  296.     BOOL useLfns = _winver > 0x0311 && lfn_api(path_) != 0;
  297. #endif
  298.         
  299.         // This loop "automagically" handles the recursive traversal of all
  300.         // subdirectories. The trick is that each scan may add new entries
  301.         // at the end, causing this loop to continue (GetSize() changes!).
  302.     
  303.     for (int i = 0; i < dirs.GetSize(); ++i)
  304.     {
  305.         CString path = fFullPath(dirs, i);
  306.         
  307.         if (handler && !handler->UpdateStatus(path))
  308.             return c4_View ();
  309.         
  310. #ifdef LFNAMES
  311.       if (useLfns)
  312.         ScanLongDir(dirs, i, path);
  313.       else
  314. #endif
  315.         ScanSubDir(dirs, i, path);
  316.     }
  317.     
  318.         // The returned object contains the entire directory tree.
  319.         // Everything is automatically destroyed when no longer referenced. 
  320.     return dirs;
  321. }
  322.  
  323. /////////////////////////////////////////////////////////////////////////////
  324. // $Id: scandisk.cpp,v 1.2 1996/12/04 14:49:33 jcw Exp $
  325.